define script AIStuff
define script RecruitArmy(nTown, nAttackSize, fDelay, fDelayVariation)
define script MoveToValidPosition(oObject, fLimit)
define script AttackTest(nTown)
define script DefendTest(nTown, nDefendSize)
define script TownManagement(nTown)
define script CreateTownArray
define script RunTownAIForAll

// Debug switch
define DEBUG = 0

// Platoon sizes.
define TINY = 10
define SMALL = 20
define MEDIUM = 80
define LARGE = 240
define LEGION = 5000

// Platoons will attack enemies within this distance.
define ATTACK_DISTANCE = 70.0

// Platoons will capture enemy towns within this distance once all enemies in area have been killed.
define CAPTURE_TOWN_DISTANCE = 60.0

// The actual number of towns on the map.
define NUMBER_OF_TOWNS = 7

// Max number of platoons in an army.
define MAX_NUMBER_OF_PLATOONS = 20
define MAX_NUMBER_OF_SIEGE_WEAPONS = 2 // Doesn't seem to work for some reason.

// The max size of the army compared to the size of the town.
define MAX_ARMY_SIZE = 30 // percent

// This array holds all town objects on the map.
global oTownArray[NUMBER_OF_TOWNS]

// MAX_NUMBER_OF_PLATOONS * NUMBER_OF_TOWNS
global oPlatoonArray[140]
global oArchersArray[140]
global oSiegeArray[140]

begin script AIStuff
start
	run script CreateTownArray
	run background script RunTownAIForAll

end script AIStuff

begin script RecruitArmy(nTown, nAttackSize, fDelay, fDelayVariation)
	oTown = get town with id nTown

	nArmySize = 0
	nMaxArmySize = 0
	nTownSize = 0
	nRecruitSize = 0
	nDiff = 0
	nCount = 0

	nAttacker = 0
	nUnderAttack = 0

	nTownRangeMin = nTown * MAX_NUMBER_OF_PLATOONS
	nTownRangeMax = (nTown * MAX_NUMBER_OF_PLATOONS) + MAX_NUMBER_OF_PLATOONS
start
	begin loop
		nAttacker = 0
		nUnderAttack = 0
		force while nAttacker < 7
			if town oTown is under takeover from player nAttacker
				nUnderAttack = 1
			end if
			nAttacker++
		end while

		if get oTown player != 0 and nUnderAttack == 0
			begin loop
				nArmySize = get army size in town oTown
				nTownSize = adult size of oTown
				nTownSize -= nArmySize
				nMaxArmySize = (nTownSize/100) * nAttackSize

				nDiff = nMaxArmySize - nArmySize

				if nDiff >= LEGION
					nRecruitSize = LEGION
				elsif nDiff >= LARGE
					nRecruitSize = LARGE
				elsif nDiff >= MEDIUM
					nRecruitSize = MEDIUM
				elsif nDiff >= SMALL
					nRecruitSize = SMALL
				elsif nDiff >= TINY
					nRecruitSize = TINY
				else
					nRecruitSize = 0
				end if

				until nRecruitSize > 0
			end loop

			// Recruit army of nRecruitSize.
			nCount = nTownRangeMin
			force while nCount < nTownRangeMax
				if oPlatoonArray[nCount] not exists
					oPlatoonArray[nCount] = recruit ARMY_UNIT_TYPE_MELEE_1 town oTown platoon of size nRecruitSize

					if oArchersArray[nCount] not exists
						oArchersArray[nCount] = recruit ARMY_UNIT_TYPE_RANGED_1 town oTown platoon of size (nRecruitSize / 4)
					end if
					add action PLATOON_AGENDA_ACTION_LINK_TO_ARMY_MEMBER using oPlatoonArray[nCount] to oArchersArray[nCount] action queue
					nCount = nTownRangeMax
				end if
				nCount++
			end while

			fDelay = number from (fDelay - fDelayVariation) to (fDelay + fDelayVariation)
			wait fDelay seconds
		end if
	end loop

end script RecruitArmy

begin script MoveToValidPosition(oObject, fLimit)
	lTarget = 0
	lFocus = 0
	fDistance = 1.0
start
	force while fDistance <= fLimit
		if not oObject can navigate to {oObject}
			lFocus = marker at focus position of oObject
			lTarget = marker at get target from {lFocus} to {oObject} distance fDistance angle 180
			set oObject position to {lTarget}
		else
			fDistance = fLimit
		end if
		fDistance++
	end while

	// Delete object if it's still stuck.
	if not oObject can navigate to {oObject}
		delete oObject
	end if

end script MoveToValidPosition

begin script AttackTest(nTown)
	oTown = get town with id nTown
	oTownCentre = get building ABODE_NUMBER_TOWN_CENTRE in oTown min built 1.0
	nTownPlayer = 0
	nPlatoonPlayer = 0
	nPlayer = 0

	oEnemyTown = 0
	oEnemyTownCentre = 0
	oFriendlyTown = 0
	oFriendlyTownCentre = 0
	oTownMisc = 0
	oTownCentreMisc = 0

	oPlatoon = 0
	oSiegeWeapon = 0
	oEnemyPlatoon = 0
	oNextEnemyPlatoon = 0
	oEnemySiegeWeapon = 0
	oNextEnemySiegeWeapon = 0

	oWall = 0
	oGate = 0
        oGate2 = 0

	fNewDistance = 0
	fLastDistance = 0
	fFriendlyNewDistance = 0
	fFriendlyLastDistance = 0

	nTownRangeMin = nTown * MAX_NUMBER_OF_PLATOONS
	nTownRangeMax = (nTown * MAX_NUMBER_OF_PLATOONS) + MAX_NUMBER_OF_PLATOONS

	nCount = 0
	nCountMisc = 0
	nSiegeCount = 0
	nAttacker = 0
	nUnderAttack = 0
	nFriendlyTownUnderAttack = 0
	nIsCapturingTown = 0

	lPos = 0
	lTarget = 0

	eEffect = 0
start
	begin loop
		//increment tribute by 1
		nTownPlayer = get oTown player
		if nTownPlayer != 0

		// Get the nearest hostile and friendly towns.
		oEnemyTown = 0
		oFriendlyTown = 0
		nCount = 0
		force while nCount < NUMBER_OF_TOWNS
			oTownMisc = oTownArray[nCount]
			if get oTownMisc player != nTownPlayer
				if oEnemyTown not exists and not town oTownMisc is under takeover from player nTownPlayer
					oEnemyTown = oTownMisc
					fLastDistance = get distance from {oTown} to {oEnemyTown}
				else
					fNewDistance = get distance from {oTown} to {oTownMisc}
					if fNewDistance < fLastDistance and not town oTownMisc is under takeover from player nTownPlayer
						fLastDistance = fNewDistance
						oEnemyTown = oTownMisc
					end if
				end if
			elsif get oTownMisc player == nTownPlayer
				if oFriendlyTown not exists
					if oTownMisc != oTown
						oFriendlyTown = oTownMisc
						fFriendlyLastDistance = get distance from {oTown} to {oFriendlyTown}
					end if
				else
					fFriendlyNewDistance = get distance from {oTown} to {oTownMisc}
					if fFriendlyNewDistance < fFriendlyLastDistance
						fFriendlyLastDistance = fFriendlyNewDistance
						oFriendlyTown = oTownMisc
					end if
				end if
			end if
			nCount++
		end while

		nCount = nTownRangeMin
		force while nCount < nTownRangeMax
			oPlatoon = oPlatoonArray[nCount]
			nPlatoonPlayer = get oPlatoon player

			// Get if the platoon is currently capturing a town. Isn't there an easier way to do this?
			nIsCapturingTown = 0
			nCountMisc = 0
			force while nCountMisc < NUMBER_OF_TOWNS
				oTownMisc = get town with id nCountMisc
				oTownCentreMisc = get building ABODE_NUMBER_TOWN_CENTRE in oTownMisc min built 1.0
				if platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentreMisc
					nIsCapturingTown = 1
					nCountMisc = NUMBER_OF_TOWNS
				end if
				nCountMisc++
			end while

			// Get town gate. Needs improvement.
                        oGate = get building ABODE_NUMBER_GATEHOUSE in oTown min built 1.0
			if oGate not exists
			oGate = get building ABODE_NUMBER_GATEHOUSE_F in oTown min built 1.0
			end if

			if oEnemyTown exists and oPlatoon exists
				oEnemyTownCentre = get building ARTEFACT_ABODE_NUMBER_TOWN_CENTRE in oEnemyTown min built 1.0
				oFriendlyTownCentre = get building ARTEFACT_ABODE_NUMBER_TOWN_CENTRE in oFriendlyTown min built 1.0

				// Check if the platoon's town is under attack.
				nAttacker = 0
				nUnderAttack = 0
				force while nAttacker < 7
					if town oTown is under takeover from player nAttacker
						nUnderAttack = 1
					end if
					nAttacker++
				end while

				// Check if the nearest friendly town is under attack.
				nAttacker = 0
				nFriendlyTownUnderAttack = 0
				force while nAttacker < 7
					if town oFriendlyTown is under takeover from player nAttacker
						nFriendlyTownUnderAttack = 1
					end if
					nAttacker++
				end while

				// Get nearest hostile platoon and siege weapon.
				oEnemyPlatoon = 0
				nPlayer = 0
				force while nPlayer < 7
					if nPlayer != nPlatoonPlayer
						if oEnemyPlatoon not exists
							oEnemyPlatoon = get platoon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fLastDistance = get distance from {oPlatoon} to {oEnemyPlatoon}
						else
							oNextEnemyPlatoon = get platoon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fNewDistance = get distance from {oPlatoon} to {oNextEnemyPlatoon}
							if fNewDistance < fLastDistance
								oEnemyPlatoon = oNextEnemyPlatoon
								fLastDistance = fNewDistance
							end if
						end if
					end if
					nPlayer++
				end while
				oEnemySiegeWeapon = 0
				nPlayer = 0
				force while nPlayer < 7
					if nPlayer != nPlatoonPlayer
						if oEnemySiegeWeapon not exists
							oEnemySiegeWeapon = get siege weapon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fLastDistance = get distance from {oPlatoon} to {oEnemySiegeWeapon}
						else
							oNextEnemySiegeWeapon = get siege weapon of player nPlayer nearest {oPlatoon} radius ATTACK_DISTANCE
							fNewDistance = get distance from {oPlatoon} to {oNextEnemySiegeWeapon}
							if fNewDistance < fLastDistance
								oEnemySiegeWeapon = oNextEnemySiegeWeapon
								fLastDistance = fNewDistance
							end if
						end if
					end if
					nPlayer++
				end while


				// This is a list of actions ordered after priority. First on the list gets the highest priority.

				// Clear action queue and attack oEnemyPlatoon if there's a hostile platoon within attacking distance.
				if oEnemyPlatoon exists
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MELEE_ATTACK_PLATOON using oEnemyPlatoon
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_MELEE_ATTACK_PLATOON using oEnemyPlatoon to oPlatoon action queue
					end if
				// Clear action queue and attack oEnemySiegeWeapon if there's a hostile siege weapon within attacking distance.
				elsif oEnemySiegeWeapon exists
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MELEE_ATTACK_MOVING_OBJECT using oEnemySiegeWeapon
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_MELEE_ATTACK_MOVING_OBJECT using oEnemySiegeWeapon to oPlatoon action queue
					end if
				// Capture hometown if it has been lost to enemy forces.
				elsif nTownPlayer != nPlatoonPlayer 
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oTownCentre to oPlatoon action queue
					end if
				// Protect hometown if it is under attack.
				elsif nUnderAttack == 1
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre to oPlatoon action queue
					end if
				// Protect nearest friendly town if it is under attack.
				elsif nFriendlyTownUnderAttack == 1
					if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oFriendlyTownCentre
						clear oPlatoon action queue
						add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oFriendlyTownCentre to oPlatoon action queue
					end if
				// Clear action queue if the platoon is protecting its hometown without reason.
				elsif nTownPlayer == nPlatoonPlayer and platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
					clear oPlatoon action queue
				// Hehehehe... This should be gooood.
				elsif nIsCapturingTown == 1
					// Do nothing! Didn't see that one coming, now did ye?
					// Note to self: Makes the platoon ignore towns that are already being captured... I think.
				// Move to nearest enemy town. Create siege weapons if target town is protected by walls.
				elsif get distance from {oPlatoon} to {oEnemyTown} > 60.0
					if oPlatoon can navigate to object oEnemyTownCentre
						if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oEnemyTownCentre
							clear oPlatoon action queue
							add action PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oEnemyTownCentre to oPlatoon action queue
						end if
					else
						if oGate exists
							add action PLATOON_AGENDA_ACTION_MOVE_TO_OBJECT using oGate to oPlatoon action queue
						else
							nSiegeCount = 0
							nCountMisc = nTownRangeMin
							force while nCountMisc < nTownRangeMax
								if oSiegeArray[nCountMisc] exists
									nSiegeCount++
								end if
								nCountMisc++
							end while

							oSiegeWeapon = oSiegeArray[nCount]
							if oSiegeWeapon not exists
								if nSiegeCount <= MAX_NUMBER_OF_SIEGE_WEAPONS
									oSiegeArray[nCount] = recruit SIEGE_BALANCE_TYPE_CATAPULT_LEVEL_1 town oTown siege weapon
								end if
							elsif oSiegeWeapon exists
								oSiegeWeapon = oSiegeArray[nCount]

								lTarget = marker at get target from {oTown} to {oEnemyTown} distance 200 angle 180
								if DEBUG == 1 eEffect = create visual effect VISUAL_EFFECT_MULTI_PICKUP at {lTarget} time -1 end if
								oWall = get wall segment nearest {lTarget} radius 400
								lTarget = marker at get target from {oSiegeWeapon} to {oWall} distance 10 angle 180

								if not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oSiegeWeapon
									add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oSiegeWeapon to oPlatoon action queue
								elsif not {oWall} is in range of oSiegeWeapon
									if not siege weapon oSiegeWeapon current action is SIEGEWEAPON_AGENDA_ACTION_MOVE_TO_POS using {lTarget}
										//clear siege weapon oSiegeArray[nCount] action queue
										add action SIEGEWEAPON_AGENDA_ACTION_MOVE_TO_POS using {lTarget} to siege weapon oSiegeWeapon action queue
									end if
								elsif not siege weapon oSiegeWeapon current action is SIEGEWEAPON_AGENDA_ACTION_ATTACK_THING using oWall
									clear siege weapon oSiegeWeapon action queue
									add action SIEGEWEAPON_AGENDA_ACTION_ATTACK_THING using oWall to siege weapon oSiegeWeapon action queue
								end if
							end if
						end if
					end if
				// Attack enemy town if there is nothing else to do.
				elsif not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oEnemyTownCentre
					clear oPlatoon action queue
					add action PLATOON_AGENDA_ACTION_ATTACK_TOWN_CENTRE_FOR_TAKE_OVER using oEnemyTownCentre to oPlatoon action queue
				end if
			else
				// Just go back and defend town centre if there are no enemy towns left.
				if nIsCapturingTown == 0 and not platoon oPlatoon current action is PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre
					clear oPlatoon action queue
					add action PLATOON_AGENDA_ACTION_DEFEND_OBJECT using oTownCentre to oPlatoon action queue
				end if
			end if

			// We don't like platoons that get stuck.
			// EDIT: Lags the script and is generally more trouble than it's worth.
			// EDIT2: Activate by holding down the 'K' button.
			if key KB_K down
				if not oPlatoon can navigate to {oPlatoon}
					run script MoveToValidPosition(oPlatoon, 20.0)
				end if
			end if

			nCount++
		end while

		end if
	end loop

end script AttackTest


// Will be used for archers on walls and other town defenses.
begin script DefendTest(nTown, nDefendSize)
start
end script DefendTest

begin script TownManagement(nTown)
	oTown = get town with id nTown
	oStore = get building ABODE_NUMBER_STORAGE_PIT in oTown min built 1.0
	oGate = 0
	oPlatoon = 0
	nPlayer = 0
start
	begin loop
		nPlayer = get oTown player
		if nPlayer != 0
			oGate = get building ABODE_NUMBER_GATEHOUSE in oTown min built 1.0
			if oGate not exists
				oGate = get building ABODE_NUMBER_GATEHOUSE_F in oTown min built 1.0
			end if

			if oGate exists
				oPlatoon = get platoon of player nPlayer nearest {oGate} radius 50.0
				if oPlatoon exists
					set gate oGate open
				else
					set gate oGate close
				end if
			end if

			// Store management. Needs improvement.
			set oStore resource RESOURCE_TYPE_FOOD to 20000
			set oStore resource RESOURCE_TYPE_WOOD to 20000
			set oStore resource RESOURCE_TYPE_ORE to 20000
		end if
	end loop

end script TownManagement

begin script CreateTownArray
	oTown = 0
	nCount = 0
start
	// Creates a town object array.
	force while nCount < NUMBER_OF_TOWNS
		oTown = get town with id nCount
		if oTown exists
			oTownArray[nCount] = oTown
		else
			nCount = NUMBER_OF_TOWNS
		end if
		nCount++
	end while

end script CreateTownArray


// This function runs all the AI scripts for all towns on the map. There's checks in each individual script to make sure human controlled towns are ignored.
begin script RunTownAIForAll
	nAttackSize = 100
	nDefendSize = 0

	fDelay = 90.0
	fDelayVariation = 30.0

	nCount = 0
start
	nAttackSize = (MAX_ARMY_SIZE/100) * nAttackSize
	nDefendSize = (MAX_ARMY_SIZE/100) * (100 - nAttackSize)

	force while nCount < NUMBER_OF_TOWNS
		run background script RecruitArmy(nCount, nAttackSize, fDelay, fDelayVariation)
		run background script AttackTest(nCount)
		run background script DefendTest(nCount, nDefendSize)
		run background script TownManagement(nCount)
		nCount++
	end while

end script RunTownAIForAll
